home *** CD-ROM | disk | FTP | other *** search
- /*------------------------------------------------------------------------------
- #
- # Apple Macintosh Developer Technical Support
- #
- # MultiFinder-Aware Simple Sample Application
- #
- # Sample
- #
- # Sample.c - C Source (main segment)
- #
- # Copyright © 1989 Apple Computer, Inc.
- # All rights reserved.
- #
- # Versions:
- # 1.00 08/88
- # 1.01 11/88
- # 1.02 04/89
- # 1.03 06/89
- # 1.04 06/92
- #
- # Components:
- # Sample.p June 1, 1989
- # Sample.c June 1, 1989
- # SampleInit.c June 2, 1992
- # Sample.a June 1, 1989
- # Sample.inc1.a June 1, 1989
- # SampleMisc.a June 1, 1989
- # Sample.r June 1, 1989
- # Sample.h June 1, 1989
- # PSample.make June 1, 1989
- # CSample.make June 1, 1989
- # ASample.make June 1, 1989
- # CSample.π June 2, 1992
- # CSample.π.rsrc June 2, 1992
- #
- # Sample is an example application that demonstrates how to
- # initialize the commonly used toolbox managers, operate
- # successfully under MultiFinder, handle desk accessories,
- # and create, grow, and zoom windows.
- #
- # It does not by any means demonstrate all the techniques
- # you need for a large application. In particular, Sample
- # does not cover exception handling, multiple windows/documents,
- # sophisticated memory management, printing, or undo. All of
- # these are vital parts of a normal full-sized application.
- #
- # This application is an example of the form of a Macintosh
- # application; it is NOT a template. It is NOT intended to be
- # used as a foundation for the next world-class, best-selling,
- # 600K application. A stick figure drawing of the human body may
- # be a good example of the form for a painting, but that does not
- # mean it should be used as the basis for the next Mona Lisa.
- #
- # We recommend that you review this program or TESample before
- # beginning a new application.
- #
- ------------------------------------------------------------------------------*/
-
-
- /* Segmentation strategy:
-
- This program consists of three segments.
- 1. "Main" contains most of the code, including the MPW libraries, and the
- main program. This segment is in the file Sample.c
- 2. "Initialize" contains code that is only used once, during startup, and
- can be unloaded after the program starts. This segment is in the file
- SampleInit.c.
- 3. "%A5Init" is automatically created by the Linker to initialize globals
- for the MPW libraries and is unloaded right away. */
-
-
- /* SetPort strategy:
-
- Toolbox routines do not change the current port. In spite of this, in this
- program we use a strategy of calling SetPort whenever we want to draw or
- make calls which depend on the current port. This makes us less vulnerable
- to bugs in other software which might alter the current port (such as the
- bug (feature?) in many desk accessories which change the port on OpenDeskAcc).
- Hopefully, this also makes the routines from this program more self-contained,
- since they don't depend on the current port setting. */
-
- #pragma segment Main
-
- #include <limits.h>
- #include "Test.h"
- #include "Sample.h" /* bring in all the #defines for Sample */
-
- /* The "g" prefix is used to emphasize that a variable is global. */
-
- /* GMac is used to hold the result of a SysEnvirons call. This makes
- it convenient for any routine to check the environment. */
- SysEnvRec gMac; /* set up by Initialize */
-
- /* GHasWaitNextEvent is set at startup, and tells whether the WaitNextEvent
- trap is available. If it is false, we know that we must call GetNextEvent. */
- Boolean gHasWaitNextEvent; /* set up by Initialize */
-
- /* GInBackground is maintained by our osEvent handling routines. Any part of
- the program can check it to find out if it is currently in the background. */
- Boolean gInBackground; /* maintained by Initialize and DoEvent */
-
-
- /* The following globals are the state of the window. If we supported more than
- one window, they would be attatched to each document, rather than globals. */
-
- /* GStopped tells whether the stop light is currently on stop or go. */
- Boolean gStopped; /* maintained by Initialize and SetLight */
-
- /* GStopRect and gGoRect are the rectangles of the two stop lights in the window. */
- Rect gStopRect; /* set up by Initialize */
- Rect gGoRect; /* set up by Initialize */
-
-
- /* Define HiWrd and LoWrd macros for efficiency. */
- #define HiWrd(aLong) (((aLong) >> 16) & 0xFFFF)
- #define LoWrd(aLong) ((aLong) & 0xFFFF)
-
- /* Define TopLeft and BotRight macros for convenience. Notice the implicit
- dependency on the ordering of fields within a Rect */
- #define TopLeft(aRect) (* (Point *) &(aRect).top)
- #define BotRight(aRect) (* (Point *) &(aRect).bottom)
-
-
-
-
- void main()
- {
- /* 1.01 - call to ForceEnvirons removed */
-
- /* If you have stack requirements that differ from the default,
- then you could use SetApplLimit to increase StackSpace at
- this point, before calling MaxApplZone. */
- MaxApplZone(); /* expand the heap so code segments load at the top */
-
- Initialize(); /* initialize the program */
- UnloadSeg((Ptr) Initialize); /* note that Initialize must not be in Main! */
-
- EventLoop(); /* call the main event loop */
- }
-
-
- /* Get events forever, and handle them by calling DoEvent.
- Get the events by calling WaitNextEvent, if it's available, otherwise
- by calling GetNextEvent. Also call AdjustCursor each time through the loop. */
-
- void EventLoop()
- {
- RgnHandle cursorRgn;
- Boolean gotEvent;
- EventRecord event;
- Point mouse;
-
- cursorRgn = NewRgn(); /* we’ll pass WNE an empty region the 1st time thru */
- do {
- /* use WNE if it is available */
- if ( gHasWaitNextEvent ) {
- GetGlobalMouse(&mouse);
- AdjustCursor(mouse, cursorRgn);
- gotEvent = WaitNextEvent(everyEvent, &event, LONG_MAX, nil);
- }
- else {
- SystemTask();
- gotEvent = GetNextEvent(everyEvent, &event);
- }
-
- if ( gotEvent ) {
- /* make sure we have the right cursor before handling the event */
- AdjustCursor(event.where, cursorRgn);
-
- if (FrontWindow() && IsDialogEvent(&event)) {
- HandleDialogEvent(&event);
- } else {
- HandleNormalEvent(&event);
- }
- }
-
- /* If you are using modeless dialogs that have editText items,
- you will want to call IsDialogEvent to give the caret a chance
- to blink, even if WNE/GNE returned FALSE. However, check FrontWindow
- for a non-NIL value before calling IsDialogEvent. */
-
- } while ( true ); /* loop forever; we quit via ExitToShell */
- } /*EventLoop*/
-
-
- /* Do the right thing for an event. Determine what kind of event it is, and call
- the appropriate routines. */
-
- void HandleNormalEvent(EventRecord *event)
- {
- short part, err;
- WindowPtr window;
- Boolean hit;
- char key;
- Point aPoint;
-
- switch ( event->what ) {
- case mouseDown:
- part = FindWindow(event->where, &window);
- switch ( part ) {
- case inMenuBar: /* process a mouse menu command (if any) */
- AdjustMenus();
- DoMenuCommand(MenuSelect(event->where));
- break;
- case inSysWindow: /* let the system handle the mouseDown */
- SystemClick(event, window);
- break;
- case inContent:
- if ( window != FrontWindow() ) {
- SelectWindow(window);
- /*HandleNormalEvent(event);*/ /* use this line for "do first click" */
- } else
- DoContentClick(window);
- break;
- case inDrag: /* pass screenBits.bounds to get all gDevices */
- DragWindow(window, event->where, &qd.screenBits.bounds);
- break;
- case inGrow:
- break;
- case inZoomIn:
- case inZoomOut:
- hit = TrackBox(window, event->where, part);
- if ( hit ) {
- SetPort(window); /* the window must be the current port... */
- EraseRect(&window->portRect); /* because of a bug in ZoomWindow */
- ZoomWindow(window, part, true); /* note that we invalidate and erase... */
- InvalRect(&window->portRect); /* to make things look better on-screen */
- }
- break;
- }
- break;
- case keyDown:
- case autoKey: /* check for menukey equivalents */
- key = event->message & charCodeMask;
- if ( event->modifiers & cmdKey ) /* Command key down */
- if ( event->what == keyDown ) {
- AdjustMenus(); /* enable/disable/check menu items properly */
- DoMenuCommand(MenuKey(key));
- }
- break;
- case activateEvt:
- DoActivate((WindowPtr) event->message, (event->modifiers & activeFlag) != 0);
- break;
- case updateEvt:
- DoUpdate((WindowPtr) event->message);
- break;
- /* 1.01 - It is not a bad idea to at least call DIBadMount in response
- to a diskEvt, so that the user can format a floppy. */
- case diskEvt:
- if ( HiWord(event->message) != noErr ) {
- SetPt(&aPoint, kDILeft, kDITop);
- err = DIBadMount(aPoint, event->message);
- }
- break;
- case kOSEvent:
- /* 1.02 - must BitAND with 0x0FF to get only low byte */
- switch ((event->message >> 24) & 0x0FF) { /* high byte of message */
- case kSuspendResumeMessage: /* suspend/resume is also an activate/deactivate */
- gInBackground = (event->message & kResumeMask) == 0;
- DoActivate(FrontWindow(), !gInBackground);
- break;
- }
- break;
- }
- } /*HandleNormalEvent*/
-
- void HandleDialogEvent(EventRecord *event)
- {
- OSErr err;
- char key;
- Point aPoint;
- short itemHit;
- DialogPtr dialog;
-
- switch ( event->what ) {
- case keyDown:
- case autoKey: /* check for menukey equivalents */
- key = event->message & charCodeMask;
- if ( event->modifiers & cmdKey ) /* Command key down */
- if ( event->what == keyDown ) {
- AdjustMenus(); /* enable/disable/check menu items properly */
- DoMenuCommand(MenuKey(key));
- }
- break;
- case diskEvt:
- if ( HiWord(event->message) != noErr ) {
- SetPt(&aPoint, kDILeft, kDITop);
- err = DIBadMount(aPoint, event->message);
- }
- break;
- default:
- if (DialogSelect(event, &dialog, &itemHit)) {
- if (itemHit == kReadySetGoButton) {
- LetTheGameBegin(dialog);
- }
- }
- }
- }
-
- /* Change the cursor's shape, depending on its position. This also calculates the region
- where the current cursor resides (for WaitNextEvent). If the mouse is ever outside of
- that region, an event would be generated, causing this routine to be called,
- allowing us to change the region to the region the mouse is currently in. If
- there is more to the event than just “the mouse moved”, we get called before the
- event is processed to make sure the cursor is the right one. In any (ahem) event,
- this is called again before we fall back into WNE. */
-
- void AdjustCursor(Point mouse, RgnHandle region)
- {
- WindowPtr window;
- RgnHandle arrowRgn;
- RgnHandle plusRgn;
- Rect globalPortRect;
-
- window = FrontWindow(); /* we only adjust the cursor when we are in front */
- if ( (! gInBackground) && (! IsDAWindow(window)) ) {
- }
- } /*AdjustCursor*/
-
-
- /* Get the global coordinates of the mouse. When you call OSEventAvail
- it will return either a pending event or a null event. In either case,
- the where field of the event record will contain the current position
- of the mouse in global coordinates and the modifiers field will reflect
- the current state of the modifiers. Another way to get the global
- coordinates is to call GetMouse and LocalToGlobal, but that requires
- being sure that thePort is set to a valid port. */
-
- void GetGlobalMouse(Point *mouse)
- {
- EventRecord event;
-
- OSEventAvail(kNoEvents, &event); /* we aren't interested in any events */
- *mouse = event.where; /* just the mouse position */
- } /*GetGlobalMouse*/
-
-
- /* This is called when an update event is received for a window.
- It calls DrawWindow to draw the contents of an application window.
- As an effeciency measure that does not have to be followed, it
- calls the drawing routine only if the visRgn is non-empty. This
- will handle situations where calculations for drawing or drawing
- itself is very time-consuming. */
-
- void DoUpdate(WindowPtr window)
- {
- if ( IsAppWindow(window) ) {
- BeginUpdate(window); /* this sets up the visRgn */
- if ( ! EmptyRgn(window->visRgn) ) /* draw if updating needs to be done */
- DrawWindow(window);
- EndUpdate(window);
- }
- } /*DoUpdate*/
-
-
- /* This is called when a window is activated or deactivated.
- In Sample, the Window Manager's handling of activate and
- deactivate events is sufficient. Other applications may have
- TextEdit records, controls, lists, etc., to activate/deactivate. */
-
- void DoActivate(WindowPtr window, Boolean becomingActive)
- {
- if ( IsAppWindow(window) ) {
- if ( becomingActive )
- /* do whatever you need to at activation */ ;
- else
- /* do whatever you need to at deactivation */ ;
- }
- } /*DoActivate*/
-
-
- /* This is called when a mouse-down event occurs in the content of a window.
- Other applications might want to call FindControl, TEClick, etc., to
- further process the click. */
-
- void DoContentClick(WindowPtr window)
- {
- } /*DoContentClick*/
-
-
- /* Draw the contents of the application window. We do some drawing in color, using
- Classic QuickDraw's color capabilities. This will be black and white on old
- machines, but color on color machines. At this point, the window’s visRgn
- is set to allow drawing only where it needs to be done. */
-
- void DrawWindow(WindowPtr window)
- {
- SetPort(window);
- } /*DrawWindow*/
-
-
- /* Enable and disable menus based on the current state.
- The user can only select enabled menu items. We set up all the menu items
- before calling MenuSelect or MenuKey, since these are the only times that
- a menu item can be selected. Note that MenuSelect is also the only time
- the user will see menu items. This approach to deciding what enable/
- disable state a menu item has the advantage of concentrating all
- the decision-making in one routine, as opposed to being spread throughout
- the application. Other application designs may take a different approach
- that is just as valid. */
-
- void AdjustMenus()
- {
- WindowPtr window;
- MenuHandle menu;
-
- window = FrontWindow();
-
- menu = GetMHandle(mFile);
- if ( IsDAWindow(window) ) /* we can allow desk accessories to be closed from the menu */
- EnableItem(menu, iClose);
- else
- DisableItem(menu, iClose); /* but not our traffic light window */
-
- menu = GetMHandle(mEdit);
- if ( IsDAWindow(window) ) { /* a desk accessory might need the edit menu… */
- EnableItem(menu, iUndo);
- EnableItem(menu, iCut);
- EnableItem(menu, iCopy);
- EnableItem(menu, iClear);
- EnableItem(menu, iPaste);
- } else { /* …but we don’t use it */
- DisableItem(menu, iUndo);
- DisableItem(menu, iCut);
- DisableItem(menu, iCopy);
- DisableItem(menu, iClear);
- DisableItem(menu, iPaste);
- }
- } /*AdjustMenus*/
-
-
- /* This is called when an item is chosen from the menu bar (after calling
- MenuSelect or MenuKey). It performs the right operation for each command.
- It is good to have both the result of MenuSelect and MenuKey go to
- one routine like this to keep everything organized. */
-
- void DoMenuCommand(long menuResult)
- {
- short menuID; /* the resource ID of the selected menu */
- short menuItem; /* the item number of the selected menu */
- short itemHit;
- Str255 daName;
- short daRefNum;
- Boolean handledByDA;
-
- menuID = HiWord(menuResult); /* use macros for efficiency to... */
- menuItem = LoWord(menuResult); /* get menu item number and menu number */
- switch ( menuID ) {
- case mApple:
- switch ( menuItem ) {
- case iAbout: /* bring up alert for About */
- itemHit = Alert(rAboutAlert, nil);
- break;
- default: /* all non-About items in this menu are DAs */
- /* type Str255 is an array in MPW 3 */
- GetItem(GetMHandle(mApple), menuItem, daName);
- daRefNum = OpenDeskAcc(daName);
- break;
- }
- break;
- case mFile:
- switch ( menuItem ) {
- case iClose:
- DoCloseWindow(FrontWindow());
- break;
- case iQuit:
- Terminate();
- break;
- }
- break;
- case mEdit: /* call SystemEdit for DA editing & MultiFinder */
- handledByDA = SystemEdit(menuItem-1); /* since we don’t do any Editing */
- break;
- }
- HiliteMenu(0); /* unhighlight what MenuSelect (or MenuKey) hilited */
- } /*DoMenuCommand*/
-
-
-
- /* Close a window. This handles desk accessory and application windows. */
-
- /* 1.01 - At this point, if there was a document associated with a
- window, you could do any document saving processing if it is 'dirty'.
- DoCloseWindow would return true if the window actually closed, i.e.,
- the user didn’t cancel from a save dialog. This result is handy when
- the user quits an application, but then cancels the save of a document
- associated with a window. */
-
- Boolean DoCloseWindow(WindowPtr window)
- {
- if ( IsDAWindow(window) )
- CloseDeskAcc(((WindowPeek) window)->windowKind);
- else if ( IsAppWindow(window) )
- CloseWindow(window);
- return true;
- } /*DoCloseWindow*/
-
-
- /**************************************************************************************
- *** 1.01 DoCloseBehind(window) was removed ***
-
- 1.01 - DoCloseBehind was a good idea for closing windows when quitting
- and not having to worry about updating the windows, but it suffered
- from a fatal flaw. If a desk accessory owned two windows, it would
- close both those windows when CloseDeskAcc was called. When DoCloseBehind
- got around to calling DoCloseWindow for that other window that was already
- closed, things would go very poorly. Another option would be to have a
- procedure, GetRearWindow, that would go through the window list and return
- the last window. Instead, we decided to present the standard approach
- of getting and closing FrontWindow until FrontWindow returns NIL. This
- has a potential benefit in that the window whose document needs to be saved
- may be visible since it is the front window, therefore decreasing the
- chance of user confusion. For aesthetic reasons, the windows in the
- application should be checked for updates periodically and have the
- updates serviced.
- **************************************************************************************/
-
-
- /* Clean up the application and exit. We close all of the windows so that
- they can update their documents, if any. */
-
- /* 1.01 - If we find out that a cancel has occurred, we won't exit to the
- shell, but will return instead. */
-
- void Terminate()
- {
- WindowPtr aWindow;
- Boolean closed;
-
- closed = true;
- do {
- aWindow = FrontWindow(); /* get the current front window */
- if (aWindow != nil)
- closed = DoCloseWindow(aWindow); /* close this window */
- }
- while (closed && (aWindow != nil));
-
- if (closed)
- ExitToShell(); /* exit if no cancellation */
- } /*Terminate*/
-
- /* Check to see if a window belongs to the application. If the window pointer
- passed was NIL, then it could not be an application window. WindowKinds
- that are negative belong to the system and windowKinds less than userKind
- are reserved by Apple except for windowKinds equal to dialogKind, which
- mean it is a dialog.
- 1.02 - In order to reduce the chance of accidentally treating some window
- as an AppWindow that shouldn't be, we'll only return true if the windowkind
- is userKind. If you add different kinds of windows to Sample you'll need
- to change how this all works. */
-
- Boolean IsAppWindow(WindowPtr window)
- {
- short windowKind;
-
- if ( window == nil )
- return false;
- else { /* application windows have windowKinds = userKind (8) */
- windowKind = ((WindowPeek) window)->windowKind;
- return ( windowKind == userKind || windowKind == dialogKind);
- }
- } /*IsAppWindow*/
-
-
- /* Check to see if a window belongs to a desk accessory. */
-
- Boolean IsDAWindow(WindowPtr window)
- {
- if ( window == nil )
- return false;
- else /* DA windows have negative windowKinds */
- return ( ((WindowPeek) window)->windowKind < 0 );
- } /*IsDAWindow*/
-
-
- /* Display an alert that tells the user an error occurred, then exit the program.
- This routine is used as an ultimate bail-out for serious errors that prohibit
- the continuation of the application. Errors that do not require the termination
- of the application should be handled in a different manner. Error checking and
- reporting has a place even in the simplest application. The error number is used
- to index an 'STR#' resource so that a relevant message can be displayed. */
-
- void AlertUser()
- {
- short itemHit;
-
- SetCursor(&qd.arrow);
- itemHit = Alert(rUserAlert, nil);
- ExitToShell();
- } /* AlertUser */